﻿using System;
using System.Collections.Generic;
using Cysharp.Threading.Tasks;
using Google.Protobuf;
using Internal.KuiHuaBaoDian.Services.Net;
using Internal.Runtime.Services.NetSimulate;
using KuiHuaBaoDian.Services;
using KuiHuaBaoDian.Services.Net;
using Runtime.Net;

namespace Runtime.Services.NetSimulate {

    /// <summary>
    /// 模拟网络服务
    /// </summary>
    public interface IDFR_NetSimulateServiceBase : INetReceiver, IDFR_NetSimulateSender {
    }

    public interface IDFR_NetSimulateSender : INetSender {

        UniTask Send(IMessage message);
    }

    /// <summary>
    /// 网络服务
    /// </summary>
    public static class NetSimulateServiceBase {

        /// <summary>
        /// 单例
        /// </summary>
        public static IDFR_NetSimulateServiceBase Instance => m_Instance ??= KHBD.TypeMap.Instantiate<IDFR_NetSimulateServiceBase>();
        private static IDFR_NetSimulateServiceBase m_Instance;
    }

    public abstract class DFR_NetSimulateServiceBase : IDFR_NetSimulateServiceBase, IService, IDisposable, IDFR_NetSimulateServiceBaseInternal {

        private sealed record NetMessage(INetPacketID ID, byte[] Body) : INetPacket;

        [AttributeUsage(AttributeTargets.Method, Inherited = false, AllowMultiple = false)]
        private sealed class BindAttribute : Attribute { }

        private readonly IDictionary<Type, Action<IMessage>> m_BindDictionary = new Dictionary<Type, Action<IMessage>>();

        protected abstract void BindMessage();

        void IDFR_NetSimulateServiceBaseInternal.Initialize() {
            m_Receiver = KHBD.TypeMap.Instantiate<INetReceiver>();
            m_Dispatcher = m_Receiver as INetReceiverInternal;

            BindMessage();
        }

        protected void AddBinding(Type type, Action<IMessage> listener) => m_BindDictionary.Add(type, listener);

        protected abstract void OnApplicationQuit();

        void IService.OnApplicationQuit() {
            OnApplicationQuit();
        }

        private INetReceiver m_Receiver;
        private INetReceiverInternal m_Dispatcher;

        void IDisposable.Dispose() {
            if (m_Receiver is IDisposable disposable) {
                disposable.Dispose();
            }
            m_Receiver = null;
            m_Dispatcher = null;
        }

        async UniTask IDFR_NetSimulateSender.Send(IMessage message) {
            var type = message.GetType();
            if (!m_BindDictionary.TryGetValue(type, out var listener)) {
                UnityEngine.Debug.LogWarning($"{type.FullName} 未在 {GetType().FullName} 中实现，或缺少[{nameof(BindAttribute)[..^9]}]标签");
                return;
            }
            await UniTask.Yield();
            listener.Invoke(message);
        }

        UniTask INetSender.Send(NetConnectionHandle connectionHandle, INetPacket message) {
            throw new NotSupportedException();
        }

        protected void Dispatch(IMessage message) {
            var packedID = DFR_NetUtility.GetPacketID(message.GetType());
            var messageID = new NetPacketID<uint>(packedID.Value);
            var netMessage = new NetMessage(messageID, message.ToByteArray());
            m_Dispatcher.Dispatch(netMessage);
        }

        void INetReceiver.AddListener(INetPacketID messageID, NetCallbackID callbackID, Action<INetPacket> callback, bool isOneTime) {
            m_Receiver.AddListener(messageID, callbackID, callback, isOneTime);
        }

        void INetReceiver.RemoveListener(INetPacketID messageID, NetCallbackID callbackID) {
            m_Receiver.RemoveListener(messageID, callbackID);
        }
    }
}

namespace Internal.Runtime.Services.NetSimulate {

    public interface IDFR_NetSimulateServiceBaseInternal {

        void Initialize();
    }
}
